package it.eng.ws.test.automata;
import it.eng.ws.test.configuration.ConfigurationManager;
import it.eng.ws.test.exceptions.UAConfigurationException;
import it.eng.ws.test.exceptions.UAExecutionException;
import it.eng.ws.test.logging.LoggingSample;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;

public class Automata {

	private static Automata instance = null;
	private static Logger l = null;
	
	static {
		if (instance == null){
			instance = new Automata();
		}
	}
	
	private HashMap<String, State> states;
	private State initialState = null;
	
	private Automata(){
		states = new HashMap<String, State>();
		if (l == null)
			l = LoggingSample.getLogger(it.eng.ws.test.automata.Automata.class);
	}
	
	public static Automata getInstance(){
		return instance;
	}
	
	public void addState(State state) throws UAConfigurationException{
		states.put(state.getLabel(), state);
		
		if (state.isInitialState() && initialState!=null)
			throw new UAConfigurationException("Duplicate initial state");
		
		if (state.isInitialState())
			initialState = state;

	}
	
	public void execute() throws UAExecutionException{
		
		State currentState = getInitialState();
		
		try {
			while (!currentState.isFinalState() && !ConfigurationManager.getInstance().isExpiredTime()){
				currentState = doStep(currentState);
			}
		} catch (UAConfigurationException e) {
			throw new UAExecutionException("Error in executing automata", e);
		}
		
	}
	
	public State getInitialState(){
		return initialState;
	}
	
	public State doStep(State currentState) throws UAExecutionException{
		l.info("eseguo... "+currentState.getLabel());
		currentState.execute();
		
		return currentState.switchToNextState();
	}
	
	public void checkConsistency() throws UAConfigurationException{
		Iterator<State> stateIterator = states.values().iterator(); 
		while(stateIterator.hasNext()){
			State state = stateIterator.next();
			state.checkConsistency();
		}
	}
	
	public State getState(String label){
		return states.get(label);
	}

	public Set<String> getStates() {
		return states.keySet();
	}
	
}
